package com.zxl.util;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Excel导出工具类，支持将List<Map>或List<实体类>导出为Excel文件
 */
public class ExcelExportUtil {
    private static final Logger logger = LoggerFactory.getLogger(ExcelExportUtil.class);
    private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
    private static final int DEFAULT_ROW_ACCESS_WINDOW_SIZE = 100;

    private ExcelExportUtil() {
        // 私有构造函数，防止实例化
    }

    /**
     * 导出Excel文件
     * @param response HTTP响应对象
     * @param fileName 导出的文件名
     * @param sheetName 工作表名称
     * @param headers 表头信息，格式为：[列名1, 列名2, ...]
     * @param fields 字段名或Map的键，格式为：[字段名1, 字段名2, ...]
     * @param dataList 数据列表，可以是List<Map>或List<实体类>
     * @throws IOException 当输出流发生错误时抛出
     */
    public static void exportExcel(HttpServletResponse response, String fileName, String sheetName,
                                  List<String> headers, List<String> fields, List<?> dataList) throws IOException {
        exportExcel(response, fileName, sheetName, headers, fields, dataList, DEFAULT_DATE_FORMAT, DEFAULT_ROW_ACCESS_WINDOW_SIZE);
    }

    /**
     * 导出Excel文件，支持自定义日期格式和内存窗口大小
     * @param response HTTP响应对象
     * @param fileName 导出的文件名
     * @param sheetName 工作表名称
     * @param headers 表头信息
     * @param fields 字段名或Map的键
     * @param dataList 数据列表
     * @param dateFormat 日期格式
     * @param rowAccessWindowSize 内存中保留的行数，超出后写入磁盘
     * @throws IOException 当输出流发生错误时抛出
     */
    public static void exportExcel(HttpServletResponse response, String fileName, String sheetName,
                                  List<String> headers, List<String> fields, List<?> dataList,
                                  String dateFormat, int rowAccessWindowSize) throws IOException {
        try (Workbook workbook = new SXSSFWorkbook(rowAccessWindowSize)) {
            Sheet sheet = workbook.createSheet(sheetName);
            // 设置响应头
            setResponseHeader(response, fileName);
            // 创建表头
            createHeaderRow(workbook, sheet, headers);
            // 填充数据
            fillDataRows(workbook, sheet, dataList, fields, dateFormat);
            // 自动调整列宽
            autoSizeColumns(sheet, headers.size());
            // 写入响应输出流
            try (OutputStream os = response.getOutputStream()) {
                workbook.write(os);
                os.flush();
            }
        } catch (Exception e) {
            logger.error("导出Excel失败", e);
            throw new IOException("导出Excel失败", e);
        }
    }

    /**
     * 设置HTTP响应头
     */
    private static void setResponseHeader(HttpServletResponse response, String fileName) {
        try {
            String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replaceAll("\\+", "%20");
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + encodedFileName + ".xlsx");
        } catch (Exception e) {
            logger.error("设置响应头失败", e);
        }
    }

    /**
     * 创建表头行
     */
    private static void createHeaderRow(Workbook workbook, Sheet sheet, List<String> headers) {
        Row headerRow = sheet.createRow(0);
        CellStyle headerStyle = createHeaderStyle(workbook);
        
        for (int i = 0; i < headers.size(); i++) {
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(headers.get(i));
            cell.setCellStyle(headerStyle);
        }
    }

    /**
     * 创建表头样式
     */
    private static CellStyle createHeaderStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        Font font = workbook.createFont();
        font.setBold(true);
        font.setFontHeightInPoints((short) 12);
        style.setFont(font);
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        style.setBorderTop(BorderStyle.THIN);
        style.setBorderRight(BorderStyle.THIN);
        style.setBorderBottom(BorderStyle.THIN);
        style.setBorderLeft(BorderStyle.THIN);
        return style;
    }

    /**
     * 填充数据行
     */
    private static void fillDataRows(Workbook workbook, Sheet sheet, List<?> dataList, 
                                    List<String> fields, String dateFormat) {
        CellStyle dataStyle = createDataStyle(workbook);
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);

        for (int i = 0; i < dataList.size(); i++) {
            Row dataRow = sheet.createRow(i + 1);
            Object data = dataList.get(i);
            
            for (int j = 0; j < fields.size(); j++) {
                Cell cell = dataRow.createCell(j);
                cell.setCellStyle(dataStyle);
                
                try {
                    Object value = null;
                    if (data instanceof Map) {
                        value = ((Map<?, ?>) data).get(fields.get(j));
                    } else {
                        value = getFieldValue(data, fields.get(j));
                    }
                    
                    if (value == null) {
                        cell.setCellValue("");
                    } else if (value instanceof Date) {
                        cell.setCellValue(sdf.format(value));
                    } else if (value instanceof Boolean) {
                        cell.setCellValue((Boolean) value ? "是" : "否");
                    } else {
                        cell.setCellValue(value.toString());
                    }
                } catch (Exception e) {
                    logger.error("填充数据失败，行: {}, 列: {}", i + 1, j + 1, e);
                    cell.setCellValue("");
                }
            }
        }
    }

    /**
     * 创建数据样式
     */
    private static CellStyle createDataStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        style.setAlignment(HorizontalAlignment.LEFT);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setBorderTop(BorderStyle.THIN);
        style.setBorderRight(BorderStyle.THIN);
        style.setBorderBottom(BorderStyle.THIN);
        style.setBorderLeft(BorderStyle.THIN);
        return style;
    }

    /**
     * 通过反射获取对象字段值
     */
    private static Object getFieldValue(Object obj, String fieldName) throws Exception {
        if (obj == null || fieldName == null || fieldName.isEmpty()) {
            return null;
        }
        
        Class<?> clazz = obj.getClass();
        Field field = null;
        
        // 尝试直接获取字段
        try {
            field = clazz.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            // 尝试获取父类字段
            try {
                field = clazz.getSuperclass().getDeclaredField(fieldName);
            } catch (NoSuchFieldException ex) {
                // 尝试使用getter方法
                String getterMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                return clazz.getMethod(getterMethodName).invoke(obj);
            }
        }
        
        field.setAccessible(true);
        return field.get(obj);
    }

    /**
     * 自动调整列宽
     */
    private static void autoSizeColumns(Sheet sheet, int columnCount) {
        for (int i = 0; i < columnCount; i++) {
            sheet.autoSizeColumn(i);
            // 增加一些额外宽度，使文本更易读
            sheet.setColumnWidth(i, sheet.getColumnWidth(i) + 2000);
        }
    }
}